#!/usr/bin/python
# OCF_RESKEY_volgrpname
# OCF_RESKEY_basename

import os, sys
import select
import subprocess
import re

# start
# stop
# monitor
# meta-data
              
# system call with arguments
def process_call_argv(argv):
        if argv:
                process = subprocess.Popen(argv, stdout=subprocess.PIPE)
        else:
                process = subprocess.Popen(argv, stdout=subprocess.PIPE)
        
        output = ""
        while True:
                out = process.stdout.readline()
                if out == '' and process.poll() != None: break
                output += out
                
        return (process.returncode, output) 

# system call with arguments
def process_call(line):
	return process_call_argv (line.strip().split(' '))

# parse config from src
def parse_config(src):
	if src == 'ietd':
		return parse_config_ietd_conf()
	elif src == 'proc':
		return parse_config_proc()

# parse config from /etc/ietd.conf
def parse_config_ietd_conf():
	(returncode, output) = process_call_argv(['cat', '/etc/ietd.conf'])
	lines = output.splitlines()
	target_exp = re.compile("^Target (.*)");
	lun_exp = re.compile("^Lun ([0-9]) (.*)");
	config = {}
	for line in lines:
		line = line.strip();
		match=target_exp.match(line)
		if match:
			target_name = match.group(1)
			#print "Target: %s" % target_name
			luns = {}
			target = {'name': target_name, 'luns': luns}
			config[target_name] = target
		else:
			match=lun_exp.match(line)
			if match:
				(lun_id, lun_params) = (match.group(1), match.group(2))
		  		#print "Lun: %s %s" % (lun_id, lun_params)
				params = {}
				for p in lun_params.split(','):
					tmp = p.split('=')
					params[tmp[0]] = tmp[1];
				# print params
				luns[lun_id] = params
	return config

# parse config from /proc/net/iet/volume
def parse_config_proc():
	(returncode, output) = process_call('cat /proc/net/iet/volume')
	lines = output.splitlines()
	# tid:1 name:iqn.san1.internal.itw:vm01
	target_exp = re.compile("^tid:[0-9]* name:(.*)");
	# lun:0 state:0 iotype:fileio iomode:wt path:/dev/mapper/vm0-vm01
	lun_exp = re.compile("^lun:([0-9]*) (.*)");
	config = {}
	for line in lines:
		line = line.strip();
		match=target_exp.match(line)
		if match:
			target_name = match.group(1)
			# print "Target: %s" % target_name
			luns = {}
			target = {'name': target_name, 'luns': luns}
			config[target_name] = target
		else:
			match=lun_exp.match(line)
			if match:
				(lun_id, lun_params) = (match.group(1), match.group(2))
		  		# print "Lun: %s %s" % (lun_id, lun_params)
				params = {}
				for p in lun_params.split(' '):
					tmp = p.split(':')
					params[tmp[0]] = tmp[1];
				# print params
				luns[lun_id] = {'Path': params['path'], 
                                'Type': params['iotype'], 
								'ScsiId': params['path'].split('-')[1]}
	return config

# build config from all volumes of a volume group
def build_config(volgrpname, basename):
	# query logical volumes of volume group
	(returncode, output) = process_call_argv(
		['lvs', 
		 '--nosuffix', 
		 '--noheadings', 
		 '--separator',  ';',  
		 '-o', 'vg_name,lv_name', 
		 volgrpname])

	# iterate logical volumes 
	# create target id per group
	# create lun for each volume	
	config = {}
	vol = 0
	lines = output.splitlines()
	lastlabel=''
	for line in lines:
		words = line.strip().split(";")
		vgname = words[0].strip()
		lvname = words[1].strip()
		label = lvname.split("_")[0]
		# print label, lvname, '/dev/mapper/' + vgname + '-' + lvname
	
		if (vol == 0 or label != lastlabel):
			luns = {}
			lun_id = 0
			target_name = "iqn.%s:%s" % (basename, label)
			target = {'name': target_name, 'luns': luns}
			config[target_name] = target

		luns[str(lun_id)] = {'Path':'/dev/mapper/%s-%s' % (vgname, lvname),
                        'Type':'blockio',
						'ScsiId':lvname}

		lastlabel = label
		lun_id = lun_id+1
		vol = vol+1
	return config

# write config in ietd.conf format
def write_config(target, config):
	# print config
	lines = []
	for name in config:
		# Target iqn.san1.internal.itw:vm01
		line = "\nTarget %s\n" % name
		lines.append(line)
		luns = config[name]['luns']
		for lun_id in luns:
			lun = luns[lun_id]
			# Lun 1 Path=/dev/mapper/vm0-vm01_swap,Type=fileio,ScsiId=vm01_swap
			line = "Lun %s Path=%s,Type=%s,ScsiId=%s\n" % (lun_id, lun['Path'], lun['Type'], lun['ScsiId'])
			lines.append(line)

	if target != '':
		f = open(target, 'wt')
		f.writelines(lines)
		f.close()
	else:
		sys.stdout.writelines(lines)

# add volumes of volgrpname to config
def add_config(config1, config2):		
	for name in config2:
		config1[name] = config2[name]
	return config1

# remove volumes of volgrpname from config
def remove_config(config1, config2):		
	for name in config2:
		if name in config1:
			del config1[name]
	return config1

# check if all targets of config2 are containd in config1
def check_config(config1, config2):		
	lines = []
	returnCode = 0
	found = 0
	for name in config2:
		if name in config1:
			found = 1
			# ok, now check the luns
			luns1 = config1[name]['luns']
			luns2 = config2[name]['luns']
			if len(luns1) != len(luns2):
				lines.append("Expecting %d but found %d LUNs for %s" % (len(luns2), len(luns1), name))
				returnCode = 2
			else: 
				for lun_id in luns2:
					lun1 = luns1[lun_id]
					lun2 = luns2[lun_id]
					path1 = lun1['Path']
					path2 = lun2['Path']
					if path1 != path2:
						lines.append("Expecting %s but found %s for %s" % (path2, path1, name))
						returnCode = 2
		else:
			# not ok
			lines.append("Missing target %s" % name)
			returnCode = 2

	if found == 1:
		if returnCode == 0:
			lines.insert(0, 'ok: started')
		elif returnCode == 2:
			lines.insert(0, 'error:')
	else:
		lines = ['ok: stopped']
		returnCode = 7

	return (returnCode, '\n'.join(lines))

# running: 0
# stopped: 3
def iscsitarget(cmd):
	(returncode, output) = process_call('rciscsitarget %s' % cmd)
	print output
	return returncode

def meta_data():
	print("""\
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="ietlvm">
<version>1.0</version>

<longdesc lang="en">
Resource script for iscsi on lvm. It manages an Linux Volume Manager volume group
as a set of iscsi targets
</longdesc>
<shortdesc lang="en">iscsi on lvm resource agent</shortdesc>

<parameters>
<parameter name="volgrpname" unique="0" required="1">
<longdesc lang="en">
The name of the volume group.
</longdesc>
<shortdesc lang="en">Volume group name</shortdesc>
<content type="string" default="" />
</parameter>
</parameters>

<actions>
<action name="start" timeout="30" />
<action name="stop" timeout="30" />
<action name="monitor" depth="0" timeout="30" interval="10" start-delay="10" />
<action name="meta-data" timeout="5" />
</actions>
</resource-agent>
""" % locals())

IETD_CONF = '/etc/ietd.conf'
# volgrpname = 'vm1'
# basename   = 'san1.internal.itw'
volgrpname = os.environ['OCF_RESKEY_volgrpname']
basename   = os.environ['OCF_RESKEY_basename']
action = sys.argv[1]

if (action == 'start'):
	print "start %s, %s" % (volgrpname, basename)
	current_config = parse_config('proc')
	write_config ('', current_config)
	new_config = add_config (current_config, build_config (volgrpname, basename))
	write_config (IETD_CONF, new_config)
	write_config ('', new_config)
	iscsitarget('reload')
	exit(0);
	# success: 0
	# error: 7

elif (action == 'stop'):
	print "stop %s" % volgrpname
	current_config = parse_config('proc')
	new_config = remove_config (current_config, build_config (volgrpname, basename))
	write_config (IETD_CONF, new_config)
	iscsitarget('reload')
	exit(0);
	# success: 0
	# error: 7

elif (action == 'monitor') or (action == 'status'):
	print "monitor %s" % volgrpname
	current_config = parse_config('proc')
	expected_config = build_config (volgrpname, basename)
	(returnCode, output) = check_config (current_config, expected_config)
	print output
	exit(returnCode);
	# running: 0
	# stopped: 7
	# error: anything else

elif (action == 'meta-data'):
	meta_data()
	exit(0);
else:
	current_config = parse_config('proc')
	print "\n## ----- Current config:"
	write_config ('', current_config)
	# print current_config
	exit (0)

	print "\n## ----- Additional targets:"
	vm_config = build_config ('vm1', 'san1.internal.itw')
	write_config ('', vm_config)

	print "\n## ----- New config:"
	new_config = add_config (current_config, vm_config)
	write_config ('', new_config)

	print "\n## ----- Old config:"
	new_config = remove_config (new_config, vm_config)
	write_config ('', new_config)

